<?php

namespace app\modules\api\controllers;

use app\controllers\BaseController;
use app\models\cache\AdminRole;
use app\models\cacheModel\AdminRoleModel;
use app\models\CommonModel;
use app\models\RequestModel;
use app\models\tableModel\AdminAuthModel;
use app\models\tableModel\AdminMenuModel;
use app\models\tableModel\AdminRoleModel as AdminRoleDBModel;
use \app\models\cacheModel\AdminRoleModel as AdminRoleCacheModel;
use Symfony\Component\Console\Helper\FormatterHelper;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;

/**
 * [权限管理]操作控制器
 */
class AdminAuthController extends BaseController
{

    /**
     * {@inheritdoc}
     */
    public function behaviors()
    {
        return [
            'verbs' => [ // 请求方式
                'class' => VerbFilter::className(),
                'actions' => [
                    'list' => ['GET'],
                    'reload' => ['GET', 'POST'],
                    'disabled' => ['POST'],
                    'open' => ['POST'],
                    'save-auth' => ['POST'],
                    'rename' => ['POST']
                ],
            ],
            'access' => [ // 是否游客可以访问
                'class' => AccessControl::className(),
                'rules' => [
                    [ // 必须登陆才能访问
                        'actions' => [
                            'list',
                            'reload',
                            'disabled',
                            'open',
                            'save-auth',
                            'rename'
                        ],
                        'allow' => true,
                        'roles' => ['@'],
                    ],
                    [ // 无需登陆即可访问
                        'actions' => [],
                        'allow' => false,
                        'roles' => ['?']
                    ]
                ]
            ]
        ];
    }

    /**
     * 列表
     * @return mixed
     */
    public function actionList()
    {

        // 及时判断 - 如果角色信息不存在
        $roleId = $this->get('roleId');

        // **** 1. 获取下当前可用的所有角色****
        $roleModel = AdminRoleDBModel::loadModel();
        // 角色列表
        $roleList = $roleModel->loadWhere([
            'status' => $roleModel::getStatOpen()
        ])->getList(1, 200, 'id, name, auth');
        $roleList = array_column($roleList, null, 'id');

        // 请求权限 条件
        $search = $this->get('search');

        $find = [];
        if (!empty($search['status'])) {
            $find[] = ['status' => $search['status']];
        }

        $model = AdminAuthModel::loadModel();

        // **** 2. 先加载下已获取的权限 ****
        // 角色的权限存在
        $haveAuth = [];
        $haveIdList = [];
        if (!empty($roleList[$roleId]['auth'])) {

            // 取出[已有父级]编号列表
            $haveParentList = array_unique(array_column($roleList[$roleId]['auth'], 'parent_id'));
            // 取出[已有子集]编号列表
            $haveIdList = array_unique(array_column($roleList[$roleId]['auth'], 'id'));
            $haveAuth = $model->loadWhere([
                'AND',
                ['IN', 'id', $haveIdList]
            ])->getList(null, array_merge([
                'AND',
                ['=', 'parent_id', '0'],
                ['IN', 'id', $haveParentList]
            ], $find));
        }

        // **** 3. 先加载下二级信息 ****
        // 获取下全部二级路由信息
        $stageList = $model::find()->where(array_merge([
            'AND',
            ['<>', 'parent_id', '0'],
            ['NOT IN', 'id', $haveIdList]
        ], $find))->asArray()->all();

        // 取出上级编号列表
        $idList = array_unique(array_column($stageList, 'parent_id'));

        // **** 3. 正式加载一级列表信息 ****
        $list = $model->loadWhere(array_merge([
            'AND',
            ['NOT IN', 'id', $haveIdList]
        ], $find))->getList(null, array_merge([
            'AND',
            ['IN', 'id', $idList]
        ], $find));

        return $this->jsonSuccess('成功', [
            'haveAuth' => $haveAuth,
            'haveNotAuth' => $list,
            'roleList' => $roleList
        ]);
    }

    /**
     * 重新加权限列表
     * @return mixed
     */
    public function actionReload()
    {

        // **** 1. 先加载下最新的路由文件信息 ****
        // 获取下全部的路由信息
        $model = AdminAuthModel::loadModel();
        // 加载模块、控制器、方法
        $model->loadModules()->loadControllers()->loadActions();
        // 如果有错误
        if ($model->hasErrors()) {

            $error = CommonModel::getModelError($model->errors);
            return $this->jsonFail($error['msg'], $error['code'], [
                'errorHint' => $error['msg']
            ]);
        }


        // **** 2. 更新控制器、方法到数据库 ****
        // 更新下路由权限信息[controller] - 以文件为基准
        if (!$model->updateController()) {

            $error = CommonModel::getModelError($model->errors);
            return $this->jsonFail($error['msg'], $error['code'], [
                'errorHint' => $error['errorHint'],
            ]);
        }

        // 更新下路由权限信息[action] - 以文件为基准
        if (!$model->updateAction()) {

            $err = CommonModel::getModelError($model->errors);
            return $this->jsonFail($err['msg'], $err['code'], [
                'errorHint' => $err['msg']
            ]);
        }

        // 返回下列表
        return $this->actionList();
    }

    /**
     * 禁用条目
     * @param array $idList 数据条目|多条数组格式，如：[1,2,3]
     * @return mixed
     */
    public function actionDisabled()
    {

        $idList = $this->post('idList');

        // 不是数组塑造下 为了构成100%的数组
        if (!empty($idList) && !is_array($idList)) $idList = explode(',', $idList);

        // 看获取的编号列表是否合法
        if (empty($idList) || count($idList) <= 0) {

            return $this->jsonFail('请选择一个条目', 400, [
                'errorHint' => '未传输任何编号',
                'errorCode' => 403
            ]);
        }

        // 检测多选条目
        $idMaxCount = empty(\Yii::$app->params['maxIdCount']) ? 100 : \Yii::$app->params['maxIdCount'];
        if (count($idList) > intval($idMaxCount)) {
            return $this->jsonFail('每次最多选择操作' . $idMaxCount . '条数据', 400);
        }

        // ********** 1、先改变自身 **********
        // 条件
        $condition = ['AND', ['IN', 'id', $idList]];
        // 修改的字段
        $field = ['status' => AdminAuthModel::getStatDisabled()];
        // 是否操作成功 - 错误一般记录到[log]日志
        if (!AdminAuthModel::updateField($condition, $field)) {

            // 将返回数据库级错误，暂不返回 会在日志中记录
            return $this->jsonFail('请求频繁请稍后尝试', 500, [
                'err_detail' => '出现致命错误。请查看日志',
                'err_code' => 500
            ]);
        }

        // ********** 2、变其子集 **********
        // 条件
        $condition = ['AND', ['IN', 'parent_id', $idList]];
        // 也要更新到下面全部的禁用
        if (!AdminAuthModel::updateField($condition, $field)) {

            // 将返回数据库级错误，暂不返回 会在日志中记录
            return $this->jsonFail('请求频繁请稍后尝试', 500, [
                'err_detail' => '出现致命错误。请查看日志',
                'err_code' => 500
            ]);
        }

        return $this->jsonSuccess('成功');
    }

    /**
     * 开启条目
     * @param array $idList 数据条目|多条数组格式，如：[1,2,3]
     * @return mixed
     */
    public function actionOpen()
    {

        $idList = $this->post('idList');

        // 不是数组塑造下 为了构成100%的数组
        if (!empty($idList) && !is_array($idList)) $idList = explode(',', $idList);

        // 看获取的编号列表是否合法
        if (empty($idList) || count($idList) <= 0) {

            return $this->jsonFail('请选择一个条目', 400, [
                'errorHint' => '未传输任何编号',
                'errorCode' => 403
            ]);
        }

        // 检测多选条目
        $idMaxCount = empty(\Yii::$app->params['maxIdCount']) ? 100 : \Yii::$app->params['maxIdCount'];
        if (count($idList) > intval($idMaxCount)) {
            return $this->jsonFail('每次最多选择操作' . $idMaxCount . '条数据', 400);
        }

        // 条件
        $condition = ['AND', ['IN', 'id', $idList]];
        // 修改的字段
        $field = ['status' => AdminAuthModel::getStatOpen()];
        // 是否操作成功 - 错误一般记录到[log]日志
        if (!AdminAuthModel::updateField($condition, $field)) {

            // 将返回数据库级错误，暂不返回 会在日志中记录
            return $this->jsonFail('请求频繁请稍后尝试', 500, [
                'err_detail' => '出现致命错误。请查看日志',
                'err_code' => 500
            ]);
        }

        // ********** 2、变其子集 **********
        // 条件
        $condition = ['AND', ['IN', 'parent_id', $idList]];
        // 也要更新到下面全部
        if (!AdminAuthModel::updateField($condition, $field)) {

            // 将返回数据库级错误，暂不返回 会在日志中记录
            return $this->jsonFail('请求频繁请稍后尝试', 500, [
                'err_detail' => '出现致命错误。请查看日志',
                'err_code' => 500
            ]);
        }

        return $this->jsonSuccess('成功');
    }

    /**
     * 重命名
     * @param array $idList 数据条目|多条数组格式，如：[1,2,3]
     * @param string $name 新名称
     * @return mixed
     */
    public function actionRename()
    {

        $idList = $this->post('idList');

        // 不是数组塑造下 为了构成100%的数组
        if (!empty($idList) && !is_array($idList)) $idList = explode(',', $idList);
        // 看获取的编号列表是否合法
        if (empty($idList) || count($idList) <= 0) {

            return $this->jsonFail('请选择一个条目', 400, [
                'errorHint' => '未传输任何编号',
                'errorCode' => 403
            ]);
        }
        // 检测多选条目
        $idMaxCount = empty(\Yii::$app->params['maxIdCount']) ? 100 : \Yii::$app->params['maxIdCount'];
        if (count($idList) > intval($idMaxCount)) {
            return $this->jsonFail('每次最多选择操作' . $idMaxCount . '条数据', 400);
        }

        // 名称
        $name = $this->post('name', 'string');
        if(empty($name)) {

            return $this->jsonFail('请输入正确的名称', 400, [
                'errorHint' => '未填写名称',
                'errorCode' => 403
            ]);
        }


        // 条件
        $condition = ['AND', ['IN', 'id', $idList]];
        // 修改的字段
        $field = ['name' => $name];
        // 是否操作成功 - 错误一般记录到[log]日志
        if (!AdminAuthModel::updateField($condition, $field)) {

            // 将返回数据库级错误，暂不返回 会在日志中记录
            return $this->jsonFail('请求频繁请稍后尝试', 500, [
                'err_detail' => '出现致命错误。请查看日志',
                'err_code' => 500
            ]);
        }

        return $this->jsonSuccess('成功');
    }

    /**
     * 保存角色权限
     * @return array
     */
    public function actionSaveAuth()
    {

        // *************** 1、角色操作 ***************
        // 获取角色编号
        $roleId = $this->post('roleId');
        // 角色不是空
        if (empty($roleId)) {
            return $this->jsonFail('请选择角色', 404, [
                'errorHint' => '未传输角色编号',
                'errorCode' => 403
            ]);
        }

        // 数据库角色信息
        $roleDetail = AdminRoleDBModel::loadModel($roleId);
        if (!$roleDetail) {
            return $this->jsonFail('请选择角色', 404, [
                'errorHint' => '角色未找到',
                'errorCode' => 403
            ]);
        }
        if ($roleDetail->getAttribute('status') != $roleDetail::getStatOpen()) {
            return $this->jsonFail('角色已禁用', 404, [
                'errorHint' => '角色已禁用',
                'errorCode' => 403
            ]);
        }


        // *************** 2、获取加载下权限操作 ***************
        // 权限编号获取
        $authIdList = $this->post('authId');
        $authIdList = empty($authIdList) ? [] : $authIdList;

        // 权限模块
        $authModel = AdminAuthModel::loadModel(true);
        // 数据
        $authList = $authModel->where([
            'AND',
            ['<>', 'parent_id', '0'],
            ['=', 'status', AdminAuthModel::getStatOpen()],
            ['IN', 'id', $authIdList]
        ])->select('id, name, route, parent_id, url')->asArray()->all();

        // 取出[URL]列表
        $childUrlList = array_column($authList, 'url');


        // *************** 3、获取加载下菜单操作 ***************
        // PS.逻辑：因为要和权限对应起来，所以过滤下，以权限为准，把没权限的菜单过滤掉
        $menuModel = AdminMenuModel::loadModel();
        $menuList = $menuModel->filterMenuByAuth($childUrlList);


        // *************** 4、角色保存数据库操作 ***************
        // 保存角色权限
        $roleDetail->setAttributes(['auth' => $authList]);
        // 保存角色权限
        $roleDetail->setAttributes(['menu' => $menuList]);
        if (!$roleDetail->saveData(true)) {
            $error = CommonModel::getModelError($roleDetail->errors);
            return $this->jsonFail($error['msg'], $error['code']);
        }

        return $this->jsonSuccess('成功');
    }
}
